package cn.geminis.crypto.core.x509;

import cn.geminis.core.util.DateUtils;
import cn.geminis.crypto.csp.AbstractSigner;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.TBSCertList;
import org.bouncycastle.cert.X509CRLEntryHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509v2CRLBuilder;

import java.io.IOException;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author puddi
 */
public class Crl {

    private X509CRLHolder bcCrl;

    private String issuer;
    private LocalDateTime nextUpdate;
    private byte[] data;

    private List<RevokeInfo> revokeInfos = new ArrayList<>();

    public String getIssuer() {
        return issuer;
    }

    public void setIssuer(String issuer) {
        this.issuer = issuer;
    }

    public LocalDateTime getNextUpdate() {
        return nextUpdate;
    }

    public void setNextUpdate(LocalDateTime nextUpdate) {
        this.nextUpdate = nextUpdate;
    }

    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public X509CRLHolder getBcCrl() {
        return this.bcCrl;
    }

    public Crl(byte[] data) {
        this.data = data;
        try {
            this.bcCrl = new X509CRLHolder(data);
        } catch (IOException e) {
            throw new RuntimeException("解析CRL错误", e);
        }
        issuer = bcCrl.getIssuer().toString();
        nextUpdate = DateUtils.toLocalDateTime(bcCrl.getNextUpdate());
    }

    public Crl() {
    }

    public Crl(X509CRLHolder holder) {
        this.bcCrl = holder;
        try {
            this.data = holder.getEncoded();
        } catch (IOException e) {
            throw new RuntimeException("解析CRL错误", e);
        }
        issuer = bcCrl.getIssuer().toString();
        nextUpdate = DateUtils.toLocalDateTime(bcCrl.getNextUpdate());
    }

    public void addRevokeInfo(BigInteger sn, Date date, int reason) {
        var revokeInfo = new RevokeInfo();
        revokeInfo.sn = sn;
        revokeInfo.date = date;
        revokeInfo.reason = reason;
        this.revokeInfos.add(revokeInfo);
    }

    public byte[] generate(AbstractSigner signer, X509Certificate issuer) {
        this.issuer = issuer.getSubject();

        var thisUpdate = DateUtils.toDate(LocalDateTime.now());
        var builder = new X509v2CRLBuilder(issuer.getBcCertificate().getSubject(), thisUpdate);
        builder.setNextUpdate(DateUtils.toDate(this.nextUpdate));
        this.revokeInfos.forEach(revokeInfo -> builder.addCRLEntry(revokeInfo.sn, revokeInfo.date, revokeInfo.reason));
        this.bcCrl = builder.build(signer.createContentSigner());
        try {
            this.data = bcCrl.getEncoded();
        } catch (IOException e) {
            throw new RuntimeException("生成CRL错误", e);
        }

        return data;
    }

    public boolean checkSn(BigInteger sn, LocalDateTime time) {
        X509CRLEntryHolder holder = bcCrl.getRevokedCertificate(sn);
        if (Objects.isNull(holder)) {
            return true;
        }

        return holder.getRevocationDate().after(DateUtils.toDate(time));
    }

    private static class RevokeInfo {
        private BigInteger sn;
        private Date date;
        private int reason;
    }

}
